/*
 * Painter.java	1.0  01/02/2006
 * Painter.java	2.0  10/02/2008
 *
 * This program proposes a painting environment to 
 * create a painting-like picture by applying customizable 
 * brushes to a real picture.
 * Author:    Isabelle Bichindaritz
 *
 * The Painter class displays an image loaded from a file,   
 * a blank image on its right, and a set of rulers and  
 * buttons underneath  
 */

import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import java.awt.image.BufferedImage;
import javax.swing.event.*;
import javax.swing.border.*;
import java.util.Vector;
import java.net.URL;
import javax.imageio.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import java.applet.Applet;


public class Painter extends JFrame  implements ActionListener {

	static int frameHeight = 0;			// height of the frame
	static Picture picture;				// original picture or OpenGL
	static Painting paint;				// new painting for OpenGL
	static GLJPanel picturePanel, paintPanel;	// GLJPanel's for the two graphics above
	static int size = 1;				// point size for OpenGL
	static int alpha = 100;
	static int fill = 10;
	static int color = 0;
	static String currentBrush = "Point";		// brush type
	static byte[] imageRGBA, paintRGBA;		// raster of the two graphics above

	private JPanel top, controls;			// panels
	private JMenuBar menuBar;			// menu bar
	private JMenuItem fileMenu;			// file menu
	private static int iWidth, iHeight, pWidth, pHeight;	// width of original and painted graphics
	private JMenuItem menuItem;			// menu item
	private String fileName = "";			// file name where the image is saved

	public void init() {
		// initialisation of the main frame of the application
		// System.out.println("Painting init");
		top  = new JPanel();
		top.setPreferredSize(new Dimension(iWidth/2, iHeight));
		getContentPane().add(top, BorderLayout.CENTER);
		top.setLayout(new GridLayout(1,2));

		GLCapabilities glCaps = new GLCapabilities();


		// creation of the buttons panel
		getContentPane().add(controls = new PictureControls( picture ), BorderLayout.SOUTH);
		
		
		// creation of the OpenGL drawable for the original picture
		picturePanel = new GLJPanel( glCaps );
		picture = new Picture(picturePanel);
		picturePanel.addGLEventListener (picture);
		imageRGBA = setPicture("images/rose2.bmp");    // file name to change here for original picture
		picture.setImage(imageRGBA, iWidth, iHeight);
		picturePanel.setPreferredSize(new Dimension(iWidth, iHeight));
		picturePanel.setSize(new Dimension(iWidth, iHeight));
		top.add (picturePanel);

		// creation of the OpenGL drawable for the painting
		paintPanel = new GLJPanel( glCaps );
		paint = new Painting(paintPanel);
		paintPanel.addGLEventListener (paint);
		paintPanel.addMouseListener (paint);
		paintPanel.addMouseMotionListener (paint);
		pWidth = iWidth;
		pHeight = iHeight;
		

		// initialization of the painting to the color white
		paintRGBA = new byte [pWidth * pHeight * 4];
		for (int k = 0; k < paintRGBA.length; k++) {
			paintRGBA[k] = (byte)Color.white.getRed();
			paintRGBA[k + 1] = (byte)Color.white.getGreen();
			paintRGBA[k + 2] = (byte)Color.white.getBlue();
			paintRGBA[k + 3] = (byte)Color.white.getAlpha();
			k = k+3;
		}
		paint.setImage(paintRGBA , pWidth, pHeight);
		paintPanel.setPreferredSize(new Dimension(iWidth, iHeight));
		top.add(paintPanel);

		// creation of the menu
		menuBar = new JMenuBar();
		menuBar.setLayout(new BoxLayout(menuBar, BoxLayout.X_AXIS));
		fileMenu = new JMenu ( "File" ) ;
		menuBar.add(fileMenu);
		menuItem = new JMenuItem("Open...");
		menuItem.setActionCommand("Open...");
		menuItem.addActionListener(this);
		fileMenu.add(menuItem);
		menuItem = new JMenuItem("Save");
		menuItem.setActionCommand("Save");
		menuItem.addActionListener(this);
		fileMenu.add(menuItem);
		menuItem = new JMenuItem("Save As...");
		menuItem.setActionCommand("Save As...");
		menuItem.addActionListener(this);
		fileMenu.add(menuItem);
		menuItem = new JMenuItem("Exit");
		menuItem.setActionCommand("Exit");
		menuItem.addActionListener(this);
		fileMenu.add(menuItem);
		menuBar.add(fileMenu);

		setJMenuBar( menuBar );
		clearPainting();

	}

	public static void main(String argv[]) {
		// main for the application
		//	System.out.println("Painting main");
		final Painter painting = new Painter();
		painting.setTitle("Painter");
		painting.init();
		painting.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {System.exit(0);}
		});
		painting.pack();
		if (imageRGBA != null) {
			painting.setSize(new Dimension(iWidth*2, iHeight + 225));
		}
		painting.setVisible(true);
		frameHeight = painting.getHeight();
	
	}

	public byte[] setPicture (String name) {  
		// loads and returns the raster for the image from the file 'name'
		// System.out.println("Painter setPicture name=" + name);   

		String imgName = name.trim(); 
		Image image;     
		if (imgName.endsWith("bmp"))
		{image = getImageBMP (imgName);}     // BMP support 
		else
		{image = getImage(imgName);};     // GIF and JPG support
		return getImageRGBA(image);
	}

	public static byte [] getOriginalPixel(Point p) {
		// returns the 3 color bytes corresponding to pixel at coordinates 'p'
		byte[] color = new byte [3];

		color[0] = imageRGBA [ (int) (p.getX()*4 + iWidth * p.getY() * 4)];
		color[1] = imageRGBA [ (int) (p.getX()*4 + (iWidth * p.getY() * 4) + 1)];
		color[2] = imageRGBA [ (int) (p.getX()*4 + (iWidth * p.getY() * 4) + 2)];

		return color;
	}

	public void clearPainting () {
		// clears the painting
		paint.clearPainting();
		paintPanel.display();
	}

	public void  paintWholeScreen() {
		// paints the painting screen entirely
		paint.paintWholeScreen();
		paintPanel.display();
	}


	public Image getImageBMP(String name) {
		// loads and returns the raster the image from the file 'name' in BMP format
		// System.out.println("Painter getImageBMP name=" + name);
		Image img = null;
		try {
			img = getImageBitmap("", name);
			MediaTracker tracker = new MediaTracker(this);
			tracker.addImage(img, 0);
			tracker.waitForID(0);
		} catch (Exception e) { System.out.println("Image not found"); }
		return img; 
	}

	public Image getImage (String name) {
		// loads and returns the image from the file 'name' in GIF format
		// System.out.println("Painter getImage name=" + name);
		Image img = Toolkit.getDefaultToolkit().createImage (name);
		return img;
	}

	public byte[] getImageRGBA (Image i) {
		// returns the raster for the image 'i'
		byte[] imageBufRGBA;
		MediaTracker tracker = new MediaTracker(new Canvas());
		tracker.addImage (i, 0);
		try {
			tracker.waitForAll();
		} catch (InterruptedException ie) {}
		iHeight = i.getHeight(null);
		iWidth = i.getWidth(null);
		//        System.out.println ("Got image, width=" +
		//                            iWidth + ", height=" + iHeight);

		// turn it into a BufferedImage
		WritableRaster raster = 
			Raster.createInterleavedRaster (DataBuffer.TYPE_BYTE,
					iWidth,
					iHeight,
					4,
					null);
		ComponentColorModel colorModel=
			new ComponentColorModel (ColorSpace.getInstance(ColorSpace.CS_sRGB),
					new int[] {8,8,8,8},
					true,
					false,
					ComponentColorModel.TRANSLUCENT,
					DataBuffer.TYPE_BYTE);            
		BufferedImage img = 
			new BufferedImage (colorModel, // color model
					raster,
					false, // isRasterPremultiplied
					null); // properties                               
		Graphics2D g = img.createGraphics();
		// use an AffineTransformation to draw upside-down in the java
		// sense, which will make it right-side-up in OpenGL
		AffineTransform gt = new AffineTransform();
		gt.translate (0, iHeight);
		gt.scale (1, -1d);
		g.transform (gt);
		g.drawImage (i, null, null);
		// now retrieve the underlying byte array from img
		DataBufferByte imageBuf = (DataBufferByte)raster.getDataBuffer();
		imageBufRGBA = imageBuf.getData();
		g.dispose();
		return imageBufRGBA;
	}



	public static boolean hasAlpha(Image image) {
		// returns true if the specified image has transparent pixels
		// System.out.println("Painter hasAlpha");
		// If buffered image, the color model is readily available
		if (image instanceof BufferedImage) {
			BufferedImage bimage = (BufferedImage)image;
			return bimage.getColorModel().hasAlpha();
		}

		// Use a pixel grabber to retrieve the image's color model;
		// grabbing a single pixel is usually sufficient
		PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
		try {
			pg.grabPixels();
		} catch (InterruptedException e) {
		}

		// Get the image's color model
		ColorModel cm = pg.getColorModel();
		return cm.hasAlpha();
	}

	public static BufferedImage toBufferedImage(Image image) {
		// returns a buffered image with the contents of an image - it is not used here
		// System.out.println("Painter toBufferedImage");
		if (image instanceof BufferedImage) {
			return (BufferedImage)image;
		}

		// This code ensures that all the pixels in the image are loaded
		image = new ImageIcon(image).getImage();

		// Determine if the image has transparent pixels; for this method's
		// implementation, see e661 Determining If an Image Has Transparent Pixels
		boolean hasAlpha = hasAlpha(image);

		// Create a buffered image with a format that's compatible with the screen
		BufferedImage bimage = null;
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		try {
			// Determine the type of transparency of the new buffered image
			int transparency = Transparency.OPAQUE;
			if (hasAlpha) {
				transparency = Transparency.BITMASK;
			}

			// Create the buffered image
			GraphicsDevice gs = ge.getDefaultScreenDevice();
			GraphicsConfiguration gc = gs.getDefaultConfiguration();
			bimage = gc.createCompatibleImage(
					image.getWidth(null), image.getHeight(null), transparency);
		} catch (HeadlessException e) {
			// The system does not have a screen
		}

		if (bimage == null) {
			// Create a buffered image using the default color model
			int type = BufferedImage.TYPE_INT_RGB;
			if (hasAlpha) {
				type = BufferedImage.TYPE_INT_ARGB;
			}
			bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
		}

		// Copy image to buffered image
		Graphics g = bimage.createGraphics();

		// Paint the image onto the buffered image
		g.drawImage(image, 0, 0, null);
		g.dispose();

		return bimage;
	}

	public static Image toImage(BufferedImage bufferedImage) {
		// this method returns and image with the content of a buffered image - it is not used here
		// System.out.println("Painter toImage");
		return Toolkit.getDefaultToolkit().createImage(bufferedImage.getSource());
	}


	public void savePaintingBMP(GLJPanel panel, String file)  {
		// saves the content of the 'panel' in a file 'file'.bmp
		// System.out.println("Painter savePaintingBMP");

		// this part requires adding in classpath jai_imageio.jar to work properly

		String name = "";
		GraphicsConfiguration gc = panel.getGraphicsConfiguration();
		//created buffered image of dimension same as plot to be saved
		BufferedImage bimg = gc.createCompatibleImage(panel.getWidth(), panel.getHeight());
		panel.paint(bimg.getGraphics()); //puts canvas into buffered image

		//used to set str to string to be saved as

		if (fileName.endsWith("bmp")) {name = fileName;}
		else {name = fileName + ".bmp"; };  
		fileName = name;
		try {
			File imageFile = new File( name );
			ImageIO.write(bimg, "bmp", imageFile); //saves files

		}
		catch (IOException e) {e.printStackTrace();};
	}


	public void savePaintingJPG(GLJPanel panel, String fileName) {
		// saves the content of the 'panel' in a file 'file'.jpg
		// System.out.println("Painter savePaintingJPG");
		String name = "";
		GraphicsConfiguration gc = panel.getGraphicsConfiguration();
		//created buffered image of dimension same as plot to be saved
		BufferedImage bimg = gc.createCompatibleImage(panel.getWidth(), panel.getHeight());
		panel.paint(bimg.getGraphics()); //puts canvas into buffered image

		//used to set str to string to be saved as

		if (fileName.endsWith("jpg")) {name = fileName;}
		else {name = fileName + ".jpg"; };  
		try {
			File imageFile = new File( name );
			ImageIO.write(bimg, "jpg", imageFile); //saves files

		}
		catch (IOException e) {e.printStackTrace();};
	}

	public Image getImageBitmap (String sdir, String sfile) {
		// returns an image from a bmp file 'sdir'/'sfile
		// System.out.println("Painter getImageBitmap");
		Image image;
		//	System.out.println("loading:"+sdir+sfile);
		try
		{
			FileInputStream fs=new FileInputStream(sdir+sfile);
			int bflen=14; // 14 byte BITMAPFILEHEADER
			byte bf[]=new byte[bflen];
			fs.read(bf,0,bflen);
			int bilen=40; // 40-byte BITMAPINFOHEADER
			byte bi[]=new byte[bilen];
			fs.read(bi,0,bilen);

			// Interpret data.
			int nsize = (((int)bf[5]&0xff)<<24) 
			| (((int)bf[4]&0xff)<<16)
			| (((int)bf[3]&0xff)<<8)
			| (int)bf[2]&0xff;
			//	System.out.println("File type is :"+(char)bf[0]+(char)bf[1]);
			//	System.out.println("Size of file is :"+nsize);

			int nbisize = (((int)bi[3]&0xff)<<24)
			| (((int)bi[2]&0xff)<<16)
			| (((int)bi[1]&0xff)<<8)
			| (int)bi[0]&0xff;
			//	System.out.println("Size of bitmapinfoheader is :"+nbisize);

			int nwidth = (((int)bi[7]&0xff)<<24)
			| (((int)bi[6]&0xff)<<16)
			| (((int)bi[5]&0xff)<<8)
			| (int)bi[4]&0xff;
			//	System.out.println("Width is :"+nwidth);

			int nheight = (((int)bi[11]&0xff)<<24)
			| (((int)bi[10]&0xff)<<16)
			| (((int)bi[9]&0xff)<<8)
			| (int)bi[8]&0xff;
			//	System.out.println("Height is :"+nheight);

			int nplanes = (((int)bi[13]&0xff)<<8) | (int)bi[12]&0xff;
			//	System.out.println("Planes is :"+nplanes);

			int nbitcount = (((int)bi[15]&0xff)<<8) | (int)bi[14]&0xff;
			//	System.out.println("BitCount is :"+nbitcount);

			// Look for non-zero values to indicate compression
			int ncompression = (((int)bi[19])<<24)
			| (((int)bi[18])<<16)
			| (((int)bi[17])<<8)
			| (int)bi[16];
			//	System.out.println("Compression is :"+ncompression);

			int nsizeimage = (((int)bi[23]&0xff)<<24)
			| (((int)bi[22]&0xff)<<16)
			| (((int)bi[21]&0xff)<<8)
			| (int)bi[20]&0xff;
			//	System.out.println("SizeImage is :"+nsizeimage);

			int nxpm = (((int)bi[27]&0xff)<<24)
			| (((int)bi[26]&0xff)<<16)
			| (((int)bi[25]&0xff)<<8)
			| (int)bi[24]&0xff;
			//	System.out.println("X-Pixels per meter is :"+nxpm);

			int nypm = (((int)bi[31]&0xff)<<24)
			| (((int)bi[30]&0xff)<<16)
			| (((int)bi[29]&0xff)<<8)
			| (int)bi[28]&0xff;
			//	System.out.println("Y-Pixels per meter is :"+nypm);

			int nclrused = (((int)bi[35]&0xff)<<24)
			| (((int)bi[34]&0xff)<<16)
			| (((int)bi[33]&0xff)<<8)
			| (int)bi[32]&0xff;
			//	System.out.println("Colors used are :"+nclrused);

			int nclrimp = (((int)bi[39]&0xff)<<24)
			| (((int)bi[38]&0xff)<<16)
			| (((int)bi[37]&0xff)<<8)
			| (int)bi[36]&0xff;
			//	System.out.println("Colors important are :"+nclrimp);

			if (nbitcount==24)
			{
				// No Palette data for 24-bit format but scan lines are
				// padded out to even 4-byte boundaries.
				int npad = (nsizeimage / nheight) - nwidth * 3;
				int ndata[] = new int [nheight * nwidth];
				byte brgb[] = new byte [( nwidth + npad) * 3 * nheight];
				fs.read (brgb, 0, (nwidth + npad) * 3 * nheight);
				int nindex = 0;
				for (int j = 0; j < nheight; j++)
				{
					for (int i = 0; i < nwidth; i++)
					{
						ndata [nwidth * (nheight - j - 1) + i] =
							(255&0xff)<<24
							| (((int)brgb[nindex+2]&0xff)<<16)
							| (((int)brgb[nindex+1]&0xff)<<8)
							| (int)brgb[nindex]&0xff;
						// System.out.println("Encoded Color at ("+i+","+j+")is:"+nrgb+
						//" (R,G,B)= ("+((int)(brgb[2]) & 0xff)+","
						//+((int)brgb[1]&0xff)+","
						//+((int)brgb[0]&0xff)+")");
						nindex += 3;
					}
					nindex += npad;
				}

				image = createImage
				( new MemoryImageSource (nwidth, nheight,
						ndata, 0, nwidth));
			}
			else if (nbitcount == 8)
			{
				// Have to determine the number of colors, the clrsused
				// parameter is dominant if it is greater than zero. If
				// zero, calculate colors based on bitsperpixel.
				int nNumColors = 0;
				if (nclrused > 0)
				{
					nNumColors = nclrused;
				}
				else
				{
					nNumColors = (1&0xff)<<nbitcount;
				}
				System.out.println("The number of Colors is"+nNumColors);

				// Some bitmaps do not have the sizeimage field calculated
				// Ferret out these cases and fix 'em.
				if (nsizeimage == 0)
				{
					nsizeimage = ((((nwidth*nbitcount)+31) & ~31 ) >> 3);
					nsizeimage *= nheight;
					//	System.out.println("nsizeimage (backup) is"+nsizeimage);
				}

				// Read the palatte colors.
				int npalette[] = new int [nNumColors];
				byte bpalette[] = new byte [nNumColors*4];
				fs.read (bpalette, 0, nNumColors*4);
				int nindex8 = 0;
				for (int n = 0; n < nNumColors; n++)
				{
					npalette[n] = (255&0xff)<<24
					| (((int)bpalette[nindex8+2]&0xff)<<16)
					| (((int)bpalette[nindex8+1]&0xff)<<8)
					| (int)bpalette[nindex8]&0xff;
					// System.out.println ("Palette Color "+n
					//+" is:"+npalette[n]+" (res,R,G,B)= ("
					//+((int)(bpalette[nindex8+3]) & 0xff)+","
					//+((int)(bpalette[nindex8+2]) & 0xff)+","
					//+((int)bpalette[nindex8+1]&0xff)+","
					//+((int)bpalette[nindex8]&0xff)+")");
					nindex8 += 4;
				}

				// Read the image data (actually indices into the palette)
				// Scan lines are still padded out to even 4-byte
				// boundaries.
				int npad8 = (nsizeimage / nheight) - nwidth;
				//	System.out.println("nPad is:"+npad8);

				int ndata8[] = new int [nwidth*nheight];
				byte bdata[] = new byte [(nwidth+npad8)*nheight];
				fs.read (bdata, 0, (nwidth+npad8)*nheight);
				nindex8 = 0;
				for (int j8 = 0; j8 < nheight; j8++)
				{
					for (int i8 = 0; i8 < nwidth; i8++)
					{
						ndata8 [nwidth*(nheight-j8-1)+i8] =
							npalette [((int)bdata[nindex8]&0xff)];
						nindex8++;
					}
					nindex8 += npad8;
				}

				image = createImage
				( new MemoryImageSource (nwidth, nheight,
						ndata8, 0, nwidth));
			}
			else
			{
				System.out.println ("Not a 24-bit or 8-bit Windows Bitmap, aborting...");
				image = (Image)null;
			}

			fs.close();
			return image;
		}
		catch (Exception e)
		{
			System.out.println("Caught exception in loadbitmap!");
		}
		return (Image) null;
	}

	public void actionPerformed (ActionEvent e) {
		// responds to menu events
		System.out.println("action performed " + e.getActionCommand());
		if ((e.getActionCommand()).equals("Open...")) { // loads a new image
			System.out.println("Menu open");
			JFileChooser fileChooser =
				new JFileChooser(".");
			int status = fileChooser.showOpenDialog(null);
			if (status == JFileChooser.APPROVE_OPTION) {
				File selectedFile = 
					fileChooser.getSelectedFile();
				System.out.println("Selected: " 
						+ selectedFile.getParent() 
						+ " --- " 
						+ selectedFile.getName());
				GLCapabilities glCaps = new GLCapabilities();
				top.remove(picturePanel);
				top.remove(paintPanel);
				picturePanel = new GLJPanel( glCaps );
				picture = new Picture(picturePanel);
				picturePanel.addGLEventListener (picture);
				imageRGBA = setPicture(selectedFile.getParent().trim() + "\\" + 
						selectedFile.getName().trim());                       //*
				picture.setImage(imageRGBA, iWidth, iHeight);
				picturePanel.setPreferredSize(new Dimension(iWidth, iHeight));  //*
				picturePanel.display();
				top.add(picturePanel);
				paintPanel = new GLJPanel( glCaps );
				paint = new Painting(paintPanel);
				paintPanel.addGLEventListener (paint);
				paintPanel.addMouseListener (paint);
				paintPanel.addMouseMotionListener (paint);
				pWidth = iWidth;
				pHeight = iHeight;
				paintRGBA = new byte [pWidth * pHeight * 4];
				for (int k = 0; k < paintRGBA.length; k++) {
					paintRGBA[k] = (byte)Color.white.getRed();
					paintRGBA[k + 1] = (byte)Color.white.getGreen();
					paintRGBA[k + 2] = (byte)Color.white.getBlue();
					paintRGBA[k + 3] = (byte)Color.white.getAlpha();
					k = k+3;
				}
				paint.setImage(paintRGBA , pWidth, pHeight);
				paintPanel.setPreferredSize(new Dimension(iWidth, iHeight));
				paintPanel.display();
				top.add (paintPanel);
				this.setSize(new Dimension(iWidth*2, iHeight + 220));    
				this.setVisible(true);
				frameHeight = this.getHeight();
				clearPainting();
			}
		}
		else  if ((e.getActionCommand()).equals("Save As...")) { // saves painting as JPG
			//		System.out.println("Menu save as");
			JFileChooser fileChooser =
				new JFileChooser(".");
			int status = fileChooser.showDialog(this, "Save");
			if (status == JFileChooser.APPROVE_OPTION) {
				File selectedFile = 
					fileChooser.getSelectedFile();
				//             		System.out.println("Selected: " 
				//                    		+ selectedFile.getParent() 
				//                    		+ " --- " 
				//                   	 + selectedFile.getName()); 
				savePaintingJPG(paintPanel, selectedFile.getParent().trim() + "/" + 
						selectedFile.getName().trim());
				//               savePaintingJPG(paintPanel, selectedFile.getParent().trim() + "/" + 
				//				    selectedFile.getName().trim());
			}
		}
		else  if ((e.getActionCommand()).equals("Save")) { // saves painting to fileName
			//	       System.out.println("Menu save");
			savePaintingJPG(paintPanel, fileName);
		}
		else  if ((e.getActionCommand()).equals("Exit")) {
			System.exit(0);

		}
	}


	/*
	 * The PictureControls class provides controls for selecting the brushing
	 * operations. 
	 */

	private class PictureControls extends JPanel implements ChangeListener, ActionListener {
		Picture picture;			      // picture associated to these controls 
		JSlider sizeSlider;			// slider for point size
		JSlider alphaSlider;
		JSlider fillSlider;
		JSlider colorSlider;
		JLabel sizeLabel;			      // size selected from slider above
		JLabel alphaLabel;
		JLabel fillLabel;
		JLabel colorLabel;
		JButton clear, paint;			// clear button and paint button

		String [] brushNames = {"Point", "Circle", "Scribble", "Dotted Line", "Spray Can", "Comb"};	// brushes available

		JComboBox  brushes;			// list of brushes

		public PictureControls(Picture picture) {
			// constructor
			// System.out.println("PictureControls constructor");

			this.picture = picture;
			setBackground(Color.gray);
			this.init();
		}

		public void init () {
			// initialization of the controls: buttons, sliders, labels, ...
			// System.out.println("PictureControls init");
			JPanel buttons = new JPanel();
			JPanel slider1 = new JPanel();
			JPanel slider2 = new JPanel();
			JPanel slider3 = new JPanel();
			JPanel slider4 = new JPanel();
			
			buttons.setLayout(new GridLayout());
			slider1.setLayout(new GridLayout(4, 1));
			slider2.setLayout(new GridLayout(4, 1));
			slider3.setLayout(new GridLayout(4, 1));
			slider4.setLayout(new GridLayout(4, 1));
		
			
			sizeSlider = new JSlider(1, 40, 1);    // set point size to 1 the first time
			sizeSlider.setMajorTickSpacing(10);
			sizeSlider.setMinorTickSpacing (5);  
			sizeSlider.setPaintTicks(true);
			sizeSlider.setPaintLabels(true);
			
			alphaSlider = new JSlider(0, 100, 100);    // set point size to 1 the first time
			alphaSlider.setMajorTickSpacing(25);
			alphaSlider.setMinorTickSpacing (0);  
			alphaSlider.setPaintTicks(true);
			alphaSlider.setPaintLabels(true);
			
			fillSlider = new JSlider(1, 50, 10);    // set point size to 1 the first time
			fillSlider.setMajorTickSpacing(5);
			fillSlider.setMinorTickSpacing (0);  
			fillSlider.setPaintTicks(true);
			fillSlider.setPaintLabels(true);
			
			//colorSlider = new JSlider(1, 40, 1);    // set point size to 1 the first time
			colorSlider = new JSlider(0, 255, 0);
			colorSlider.setMajorTickSpacing(50);
			colorSlider.setMinorTickSpacing (20);  
			colorSlider.setPaintTicks(true);
			colorSlider.setPaintLabels(true);
			
			setLayout(new BorderLayout());
			sizeLabel = new JLabel("   " + sizeSlider.getValue() + "   ");
			alphaLabel = new JLabel("   " + alphaSlider.getValue() + "   ");
			fillLabel = new JLabel("   " + fillSlider.getValue() + "   ");
			colorLabel = new JLabel("   " + colorSlider.getValue() + "   ");
			
			slider1.add(sizeLabel);
			slider1.add(alphaLabel);
			slider1.add(fillLabel);
			slider1.add(colorLabel);
			add("West", slider1);
			
			
			slider2.add( sizeSlider);
			slider2.add(alphaSlider);
			slider2.add(fillSlider);
			slider2.add(colorSlider);
			add("Center", slider2);
			
			slider3.add(new JLabel("   size   "));
			slider3.add(new JLabel("   alpha  "));
			slider3.add(new JLabel("   fill  "));
			slider3.add(new JLabel(" color bend "));
			add("East", slider3);
			
			clear = new JButton("Clear");
			paint = new JButton("Paint");
			brushes = new JComboBox(brushNames);
			brushes.setBorder(BorderFactory.createLoweredBevelBorder());
			brushes.getModel().setSelectedItem(brushes.getModel().getElementAt(0));
			buttons.add(brushes);
			buttons.add(clear);
			buttons.add(paint);
			add("North", buttons);
			sizeSlider.addChangeListener( this );
			alphaSlider.addChangeListener( this );
			fillSlider.addChangeListener( this );
			colorSlider.addChangeListener( this );
						
			clear.addActionListener( this );
			paint.addActionListener( this );
			brushes.addActionListener( this );
		}

		public void stateChanged (ChangeEvent e) {
			// answers to the events for the buttons and sliders
			   // size slider selected
			//   System.out.println("PictureControls slider");
			if (e.getSource().equals(sizeSlider)) {
				sizeLabel.setText( "   " + sizeSlider.getValue() + "   ");
				size = sizeSlider.getValue();
			} else if (e.getSource().equals(alphaSlider)) {
				alphaLabel.setText( "   " + alphaSlider.getValue() + "   ");
				alpha = alphaSlider.getValue();
			} else if (e.getSource().equals(fillSlider)) {
				fillLabel.setText( "   " + fillSlider.getValue() + "   ");
				fill = fillSlider.getValue();
			} else if (e.getSource().equals(colorSlider)) {
				colorLabel.setText( "   " + colorSlider.getValue() + "   ");
				color = colorSlider.getValue();
			}
		}

		public void actionPerformed (ActionEvent e) {
			// answer to the events for the brush selection
			if (e.getSource().equals(brushes)) { // brush selected
				currentBrush = ((String)brushes.getSelectedItem()).trim();
				System.out.println("Current brush changed to " + currentBrush);
			}
			else if (e.getSource().equals(clear)) {  // clear button pressed
				//		      System.out.println("PictureControls clear painting");
				clearPainting();
			}
			else if (e.getSource().equals(paint)) {  // paint button pressed
				//		      System.out.println("PictureControls paint painting");
				paintWholeScreen();
			}
		}

	} // end PictureControls class


}  // end Painter class



